/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef CAN_DRV_H
#define CAN_DRV_H

/*!
 * @file can_drv.h
 * @brief This file declares can driver interfaces
 */

/*!
 * @addtogroup can_drv
 * @{
 */

/*******Includes***************************************************************/
#include <stddef.h>
#include <stdbool.h>
#include "device_registers.h"
#include "errcodes.h"
#include "os_adapter.h"
#include "clock_drv.h"

/*******Definitions************************************************************/
/*!
 * @brief CAN irq param
 */
#define CAN_INTERRUPT_EIE       (0x00000002UL)     /*!< Error interrupt enable */
#define CAN_INTERRUPT_TSIE      (0x00000004UL)     /*!< Transmit secondary interrupt enable */
#define CAN_INTERRUPT_TPIE      (0x00000008UL)     /*!< Transmit priority interrupt enable */
#define CAN_INTERRUPT_RAFIE     (0x00000010UL)     /*!< Receive almost full interrupt enable */
#define CAN_INTERRUPT_RFIE      (0x00000020UL)     /*!< Receive full interrupt enable */
#define CAN_INTERRUPT_ROIE      (0x00000040UL)     /*!< Receive overflow interrupt enable */
#define CAN_INTERRUPT_RIE       (0x00000080UL)     /*!< Receive interrupt enable */
#define CAN_INTERRUPT_BEIE      (0x00020000UL)     /*!< Bus error interrupt enable */
#define CAN_INTERRUPT_ALIE      (0x00080000UL)     /*!< Arbriation lost interrupt enable */
#define CAN_INTERRUPT_EPIE      (0x00200000UL)     /*!< Error passative interrupt enable */
/*!
 * @brief CAN event type
 */
typedef enum {
    CAN_EVENT_NONE            = 0x00U, /*!< No Event */
    CAN_EVENT_RX_COMPLETE     = 0x01U, /*!< A frame was received in RX fifo */
    CAN_EVENT_RX_WARNING      = 0x02U, /*!< RX fifo is almost full */
    CAN_EVENT_RX_FULL         = 0x03U, /*!< RX fifo is full */
    CAN_EVENT_RX_OVERFLOW     = 0x04U, /*!< RX fifo is full (incoming message was
                                            lost or cover oldest.define by ROM register) */
    CAN_EVENT_PTB_TX_COMPLETE = 0x05U, /*!< A frame in PTB was sent */
    CAN_EVENT_STB_TX_COMPLETE = 0x06U, /*!< All frames in STB was sent */
    CAN_EVENT_BUSOFF          = 0x07U, /*!< A busoff event was occured*/
    CAN_EVENT_ERR             = 0x08U, /*!< A error event was occured*/
    CAN_EVENT_BUS_ERROR       = 0x09U, /*!< A bus error event was occured*/
    CAN_EVENT_ARB_LOST        = 0x0AU, /*!< A arbitration lost event was occured*/
    CAN_EVENT_ERR_PASSIVE     = 0x0BU, /*!< A passive error event was occured*/
    CAN_EVENT_ABORT           = 0x0CU, /*!< A abort event was occured*/
} can_event_t;

/*!
 * @brief CAN error type
 */
typedef enum {
    CAN_ERR_NONE           = 0x00U, /*!< No error */
    CAN_ERR_BIT_ERROR      = 0x01U, /*!< Bit error */
    CAN_ERR_FORM_ERROR     = 0x02U, /*!< Form error */
    CAN_ERR_STUFF_ERROR    = 0x03U, /*!< Stuff error */
    CAN_ERR_ACK_ERROR      = 0x04U, /*!< Acknowledgement error */
    CAN_ERR_CRC_ERROR      = 0x05U, /*!< CRC error */
    CAN_ERR_OTHER_ERROR    = 0x06U, /*!< Other error */
} can_error_t;

/*!
 * @brief Callback for CAN instance when event occured
 */
typedef void (*can_callback_t)(uint32_t instance, can_event_t event, void* param);

/*!
 * @brief CAN operation mode
 */
typedef enum {
    CAN_NORMAL_MODE            = 0x00U, /*!< Normal mode */
    CAN_LISTEN_ONLY_MODE       = 0x01U, /*!< Listen only mode */
    CAN_LOOPBACK_EXTERNAL_MODE = 0x02U, /*!< Loop back external mode */
    CAN_LOOPBACK_INTERNAL_MODE = 0x03U, /*!< Loop back internal mode */
} can_mode_t;

/*!
 * @brief CAN message buffer state
 */
typedef enum {
    CAN_MB_IDLE    = 0x00U, /*!< MB is empty */
    CAN_MB_RX_BUSY = 0x01U, /*!< MB is used for reception */
    CAN_MB_TX_BUSY = 0x02U  /*!< MB is used for transmission */
} can_mb_state_t;

/*!
 * @brief CAN transmit buffer type
 */
typedef enum {
    CAN_PRIMARY_TRANSMIT_BUFFER   = 0x00U, /*!< Transmit use PTB */
    CAN_SECONDARY_TRANSMIT_BUFFER = 0x01U, /*!< Transmit use STB */
    CAN_SECONDARY_SINGLE_TRANSMIT = 0x02U  /*!< Transmit use STB sigle way */
} can_buffer_type_t;

/*!
 * @brief CAN time quantum segment set
 */
typedef struct {
    uint8_t prescaler;  /*!< Pre scaler */
    uint8_t phaseSeg1;  /*!< Phase segment 1 */
    uint8_t phaseSeg2;  /*!< Phase segment 2 */
    uint8_t rJumpwidth; /*!< Resync jump width */
} can_time_segment_t;

/*!
 * @brief CAN receive filter config struct
 */
typedef struct {
    uint32_t code; /*!< Filter code */
    uint32_t mask; /*!< Filter mask */
} can_filter_config_t;

/*!
 * @brief CAN message info struct
 */
typedef struct {
    uint8_t IDE;        /*!< Id type, standard/extend message */
    uint8_t FDF;        /*!< CAN FD indicator */
    uint8_t BRS;        /*!< CAN FD data rate switch indicator */
    uint8_t RTR;        /*!< Remote frame indicator */
    uint8_t dataLength; /*!< Data length */
} can_data_info_t;

/*!
 * @brief CAN message info struct
 */
typedef struct {
    uint32_t msgId;   /*!< CAN message id */
    uint32_t cs;      /*!< Control and Status */
    uint8_t data[64]; /*!< Data bytes for CAN message */
    uint8_t dataLen;  /*!< Length of data in bytes */
} can_msgbuff_t;

/*!
 * @brief Information for internal handling of msg buffer.
 */
typedef struct {
    semaphore_t mbSema;   /*!< Semaphore used for signaling completion of a blocking transfer */
    can_mb_state_t state; /*!< The state of the message buffer */
    bool isBlocking;      /*!< True if the transfer is blocking */
} can_mb_handle_t;

/*!
 * @brief CAN driver state context
 *
 * @note The contents of this structure is used to driver and should not be
 *       modified by users.
 */
typedef struct {
    can_mb_handle_t mbs[CONFIG_CAN_MAX_MB_NUM]; /*!< Message buffer handle */
    can_callback_t callback;                    /*!< Callback */
    void* callbackParam;                        /*!< Callback param*/
} can_state_t;

/*!
 * @brief CAN configuration struct
 */
typedef struct {
    can_mode_t mode;              /*!< CAN operation mode */
    bool fdEnable;                /*!< CAN FD mode set */
    bool busOffAutoRec;           /*!< CAN bus off mode set */
    can_clk_src_t srcClk;         /*!< Clock source of CAN function */
    can_time_segment_t bitrate;   /*!< The bitrate used for classical CAN or for the nominal field of CAN FD */
    can_time_segment_t bitrateFd; /*!< The bitrate used for the data field of CAN FD */
    uint32_t warnValue;           /*!< CAN receive fifo almost ful warning value*/
    can_callback_t callback;      /*!< Callback */
    void* callbackParam;          /*!< Callback param*/
} can_config_t;

/*******APIs*******************************************************************/
#if defined(__cplusplus)
extern "C" {
#endif

/*!
 * @brief Get a default configuration structure for CAN
 *
 * @note This function get a default configuration structure config with the following settings
 *       - CAN normal mode
 *       - CAN FD disabled
 *       - Function clock = HSE clock
 *       - Bit rate of classical CAN is 500Kbit/s, sample point = 81.25 (HSE clock is 8M)
 * @param[out] canConfig: A pointer to can config data
 * @return None
 */
void CAN_GetDefaultConfig(can_config_t* canConfig);

/*!
 * @brief Initialize the CAN peripheral.
 *
 * @param[in] instance: A CAN instance number
 * @param[out] state: Point to CAN driver state struct
 * @param[in] canConfig: Point to CAN config data
 * @return ERR_SUCCESS : Init successful
 *         ERR_ERROR : Init error
 */
errcode_t CAN_Init(uint32_t instance, can_state_t* state, const can_config_t* canConfig);

/*!
 * @brief Configure the can interrupt Enable.
 *
 * @param[in] instance: A CAN instance number
 * @param[in] mask: Decided which interrupt ought to be configured
              User can enable/disable one or more interrupt once
              Eg: CAN_INTERRUPT_EIE or (CAN_INTERRUPT_EIE | CAN_INTERRUPT_RAFIE | ...)
 * @param[in] enable: Enable the interrupt or disable the interrupt
 * @return None
 */
void CAN_IrqConfig(uint32_t instance, uint32_t mask, bool enable);

/*!
 * @brief Shut down a CAN instance
 *
 * @param[in] instance: A CAN instance number
 * @return None
 */
void CAN_Deinit(uint32_t instance);

/*!
 * @brief Set bit rate of classical CAN and nominal field of CAN FD
 *
 * @param[in] instance: A CAN instance number
 * @param[in] bitrate: Pointer to bit rate config data
 * @return None
 */
void CAN_SetBitrate(uint32_t instance, const can_time_segment_t* bitrate);

/*!
 * @brief Get bit rate config of classical CAN and nominal field of CAN FD
 *
 * @param[in] instance: A CAN instance number
 * @param[out] bitrate: Pointer to a buffer for storage the bit rate settings
 * @return None
 */
void CAN_GetBitrate(uint32_t instance, can_time_segment_t* bitrate);

/*!
 * @brief Set bit rate of data field of CAN FD
 *
 * @param[in] instance: A CAN instance number
 * @param[in] bitrate: Pointer to bit rate config data
 * @return None
 */
void CAN_SetBitrateFD(uint32_t instance, const can_time_segment_t* bitrate);

/*!
 * @brief Get bit rate of data field of CAN FD
 *
 * @param[in] instance: A CAN instance number
 * @param[out] bitrate: Pointer to a buffer for storage the bit rate settings
 * @return None
 */
void CAN_GetBitrateFD(uint32_t instance, can_time_segment_t* bitrate);

/*!
 * @brief Set receive filter
 *
 * @param[in] instance: A CAN instance number
 * @param[in] filterNum: Number of filter, range 1~16, No filter used while cause no frame received
 * @param[in] filterTable: A pointer to filter config data
 * @return None
 */
void CAN_SetRxFilter(uint32_t instance, uint32_t filterNum, const can_filter_config_t* filterTable);

/*!
 * @brief Set transceiver delay compensation
 *
 * @param[in] instance: A CAN instance number
 * @param[in] enable: Enable/Disable TDC
 * @param[in] offset: TDC offset value
 * @return None
 */
void CAN_SetTDCOffset(uint32_t instance, bool enable, uint8_t offset);

/*!
 * @brief Get transceiver delay compensation offset value
 *
 * @param[in] instance: A CAN instance number
 * @return TDC offset value
 */
uint8_t CAN_GetTDCOffset(uint32_t instance);

/*!
 * @brief Get transmit error count
 *
 * @param[in] instance: A CAN instance number
 * @return TEC value
 */
uint8_t CAN_GetTEC(uint32_t instance);

/*!
 * @brief Get receive error count
 *
 * @param[in] instance: A CAN instance number
 * @return REC value
 */
uint8_t CAN_GetREC(uint32_t instance);

/*!
 * @brief Get error type
 *
 * @param[in] instance: A CAN instance number
 * @return Error value
 */
can_error_t CAN_GetErrType(uint32_t instance);

/*!
 * @brief Send a CAN frame using a blocking method
 *
 * @param[in] instance: A CAN instance number
 * @param[in] type: Send buffer type
 *            - CAN_PRIMARY_TRANSMIT_BUFFER
 *            - CAN_SECONDARY_TRANSMIT_BUFFER
 * @param[in] info: Send message info
 * @param[in] msgId: Send message id
 * @param[in] data: Send message data
 * @param[in] timeout: Timeout for transfer in millisenconds
 * @return ERR_SUCCESS : Send successful
 *         ERR_BUSY : The resource is busy
 *         ERR_TIMEOUT : Transmit timeout
 *         ERR_ERROR : Other error occurred
 */
errcode_t CAN_SendBlocking(uint32_t instance,
                           can_buffer_type_t type,
                           const can_data_info_t* info,
                           uint32_t msgId,
                           const uint8_t* data,
                           uint32_t timeout);

/*!
 * @brief Send a CAN frame without waiting result
 *
 * @param[in] instance: A CAN instance number
 * @param[in] type: Send buffer type
 *            - CAN_PRIMARY_TRANSMIT_BUFFER
 *            - CAN_SECONDARY_TRANSMIT_BUFFER
 * @param[in] info: Send message info
 * @param[in] msgId: Send message id
 * @param[in] data: Send message data
 * @return ERR_SUCCESS : Send successful
 *         ERR_BUSY : The resource is busy
 *         ERR_ERROR : Other error occurred
 */
errcode_t CAN_Send(uint32_t instance,
                   can_buffer_type_t type,
                   const can_data_info_t* info,
                   uint32_t msgId,
                   const uint8_t* data);

/*!
 * @brief Receive a CAN frame using a blocking method
 *
 * @param[in] instance: A CAN instance number
 * @param[out] data: CAN receive frame buffer
 * @param[in] timeout_ms: Timeout for receive in millisenconds
 * @return ERR_SUCCESS : Receive successful
 *         ERR_TIMEOUT : Receive timeout
 *         ERR_ERROR : Other error occurred
 */
errcode_t CAN_ReceiveBlocking(uint32_t instance,
                              can_msgbuff_t* data,
                              uint32_t timeout_ms);

/*!
 * @brief Receive a CAN frame.
 *
 * @param[in] instance: A CAN instance number
 * @param[out] data: CAN receive frame buffer
 * @return ERR_SUCCESS : Receive successful
 *         ERR_CAN_RXFIFO_EMPTY : Receive fifo is empty
 */
errcode_t CAN_Receive(uint32_t instance, can_msgbuff_t* data);

/*!
 * @brief Abort a transfer
 *
 * @param[in] instance: A CAN instance number.
 * @param[in] type: Abort send buffer type
 *            - CAN_PRIMARY_TRANSMIT_BUFFER
 *            - CAN_SECONDARY_TRANSMIT_BUFFER
 * @return None
 */
void CAN_AbortTransfer(uint32_t instance, can_buffer_type_t type);

/*!
 * @brief Return whether a transfer is finish
 *
 * @param[in] instance: A CAN instance number
 * @param[in] type: Transfer buffer type
 *            - CAN_PRIMARY_TRANSMIT_BUFFER
 *            - CAN_SECONDARY_TRANSMIT_BUFFER
 * @return ERR_SUCCESS : The resource is idle
 *         ERR_BUSY : The resource is busy
 */
errcode_t CAN_GetTransferStatus(uint8_t instance, can_buffer_type_t type);

/*!
 * @brief Return whether passive error or not
 *
 * @param[in] instance: A CAN instance number
 * @return true : The instance is passive error
 *         false : The instance is active error
 */
bool CAN_GetPassiveErrorStatus(uint8_t instance);

/*!
 * @brief Set a callback function for CAN IRQ handler
 *
 * @param[in] instance: A CAN instance number
 * @param[in] callback: The callback function
 * @param[in] param: Param for user
 * @return None
 */
void CAN_InstallCallback(uint32_t instance, can_callback_t callback, void* param);

/*!
 * @brief Interrupt handler for a CAN instance
 *
 * @param[in] instance: A CAN instance number
 * @return None
 */
void CAN_IRQHandlerProcess(uint32_t instance);

#if defined(__cplusplus)
}
#endif

/*! @} */

#endif /* CAN_DRV_H */

/*******EOF********************************************************************/
